 /*******************************************************************************
  * Copyright (c) 2004, 2006 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.ui.internal.themes;

 import java.util.Collection ;
 import java.util.HashMap ;
 import java.util.HashSet ;
 import java.util.Map ;
 import java.util.ResourceBundle ;

 import org.eclipse.core.runtime.IConfigurationElement;
 import org.eclipse.core.runtime.IExtensionRegistry;
 import org.eclipse.core.runtime.Platform;
 import org.eclipse.jface.resource.StringConverter;
 import org.eclipse.ui.PlatformUI;
 import org.eclipse.ui.internal.WorkbenchPlugin;
 import org.eclipse.ui.internal.registry.IWorkbenchRegistryConstants;
 import org.eclipse.ui.internal.registry.RegistryReader;
 import org.eclipse.ui.themes.IColorFactory;

 /**
  * Registry reader for themes.
  *
  * @since 3.0
  */
 public class ThemeRegistryReader extends RegistryReader {

     /**
      * The translation bundle in which to look up internationalized text.
      */
     private final static ResourceBundle RESOURCE_BUNDLE = ResourceBundle
             .getBundle(ThemeRegistryReader.class.getName());

     private Collection categoryDefinitions = new HashSet ();

     private Collection colorDefinitions = new HashSet ();

     private Collection fontDefinitions = new HashSet ();

     private ThemeDescriptor themeDescriptor = null;

     private ThemeRegistry themeRegistry;

     private Map dataMap = new HashMap ();

     /**
      * ThemeRegistryReader constructor comment.
      */
     public ThemeRegistryReader() {
         super();
     }

     /**
      * Returns the category definitions.
      *
      * @return the category definitions
      */
     public Collection getCategoryDefinitions() {
         return categoryDefinitions;
     }

     /**
      * Returns the color definitions.
      *
      * @return the color definitions
      */
     public Collection getColorDefinitions() {
         return colorDefinitions;
     }

     /**
      * Returns the data map.
      *
      * @return the data map
      */
     public Map getData() {
         return dataMap;
     }

     /**
      * Returns the font definitions.
      *
      * @return the font definitions
      */
     public Collection getFontDefinitions() {
         return fontDefinitions;
     }

     /**
      * Read a category.
      *
      * @param element the element to read
      * @return the new category
      */
     private ThemeElementCategory readCategory(IConfigurationElement element) {
         String name = element.getAttribute(IWorkbenchRegistryConstants.ATT_LABEL);

         String id = element.getAttribute(IWorkbenchRegistryConstants.ATT_ID);
         String parentId = element.getAttribute(IWorkbenchRegistryConstants.ATT_PARENT_ID);

         String description = null;

         IConfigurationElement[] descriptions = element
                 .getChildren(IWorkbenchRegistryConstants.TAG_DESCRIPTION);

         if (descriptions.length > 0) {
             description = descriptions[0].getValue();
         }

         return new ThemeElementCategory(name, id, parentId, description,
                 element.getNamespace(), element);
     }

     /**
      * Read a color.
      *
      * @param element the element to read
      * @return the new color
      */
     private ColorDefinition readColor(IConfigurationElement element) {
         String name = element.getAttribute(IWorkbenchRegistryConstants.ATT_LABEL);

         String id = element.getAttribute(IWorkbenchRegistryConstants.ATT_ID);

         String defaultMapping = element.getAttribute(IWorkbenchRegistryConstants.ATT_DEFAULTS_TO);

         String value = getPlatformSpecificColorValue(element
                 .getChildren(IWorkbenchRegistryConstants.TAG_COLORVALUE));

         if (value == null) {
             value = getColorValue(element);
         }

         if ((value == null && defaultMapping == null)
                 || (value != null && defaultMapping != null)) {
             logError(element, RESOURCE_BUNDLE.getString("Colors.badDefault")); //$NON-NLS-1$
 return null;
         }

         String categoryId = element.getAttribute(IWorkbenchRegistryConstants.ATT_CATEGORY_ID);

         String description = null;
         boolean isEditable = true;
         String isEditableString = element.getAttribute(IWorkbenchRegistryConstants.ATT_IS_EDITABLE);
         if (isEditableString != null) {
             isEditable = Boolean.valueOf(isEditableString).booleanValue();
         }

         IConfigurationElement[] descriptions = element
                 .getChildren(IWorkbenchRegistryConstants.TAG_DESCRIPTION);

         if (descriptions.length > 0) {
             description = descriptions[0].getValue();
         }

         return new ColorDefinition(name, id, defaultMapping, value, categoryId,
                 isEditable, description, element.getDeclaringExtension()
                         .getNamespace());
     }

     /**
      * Gets the color value, either via the value attribute or from a color
      * factory.
      *
      * @param element the element to check
      * @return the color string
      */
     private String getColorValue(IConfigurationElement element) {
         if (element == null) {
             return null;
         }

         String value = element.getAttribute(IWorkbenchRegistryConstants.ATT_VALUE);
         if (value == null) {
             value = checkColorFactory(element);
         }
         return value;
     }

     /**
      * Check for platform specific color values. This will return the
      * "best match" for the current platform.
      *
      * @param elements the elements to check
      * @return the platform specific color, if any
      */
     private String getPlatformSpecificColorValue(
             IConfigurationElement[] elements) {
         return getColorValue(getBestPlatformMatch(elements));
     }

     /**
      * Get the element that has os/ws attributes that best match the current
      * platform.
      *
      * @param elements the elements to check
      * @return the best match, if any
      */
     private IConfigurationElement getBestPlatformMatch(
             IConfigurationElement[] elements) {
         IConfigurationElement match = null;

         String osname = Platform.getOS();
         String wsname = Platform.getWS();

         for (int i = 0; i < elements.length; i++) {
             IConfigurationElement element = elements[i];
             String elementOs = element.getAttribute(IWorkbenchRegistryConstants.ATT_OS);
             String elementWs = element.getAttribute(IWorkbenchRegistryConstants.ATT_WS);

             if (osname.equalsIgnoreCase(elementOs)) {
                 if (wsname.equalsIgnoreCase(elementWs)) {
                     // best possible match. Return
 return element;
                 }
                 match = element;
             } else if (wsname.equalsIgnoreCase(elementWs)) {
                 match = element;
             }
         }

         return match;
     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.internal.registry.RegistryReader#readElement(org.eclipse.core.runtime.IConfigurationElement)
      */
     public boolean readElement(IConfigurationElement element) {
         String elementName = element.getName();
         if (themeDescriptor == null && elementName.equals(IWorkbenchRegistryConstants.TAG_COLORDEFINITION)) {
             ColorDefinition definition = readColor(element);
             if (definition != null) {
                 if (!colorDefinitions.contains(definition)) {
                     colorDefinitions.add(definition);
                     themeRegistry.add(definition);
                 }
             }
             return true;
         } else if (themeDescriptor != null
                 && elementName.equals(IWorkbenchRegistryConstants.TAG_COLOROVERRIDE)) {
             ColorDefinition definition = readColor(element);
             if (definition != null) {
                 themeDescriptor.add(definition);
             }
             return true;
         } else if (themeDescriptor == null
                 && elementName.equals(IWorkbenchRegistryConstants.TAG_FONTDEFINITION)) {
             FontDefinition definition = readFont(element);
             if (definition != null) {
                 if (!fontDefinitions.contains(definition)) {
                     fontDefinitions.add(definition);
                     themeRegistry.add(definition);
                 }
             }
             return true;
         } else if (themeDescriptor != null
                 && elementName.equals(IWorkbenchRegistryConstants.TAG_FONTOVERRIDE)) {
             FontDefinition definition = readFont(element);
             if (definition != null) {
                 themeDescriptor.add(definition);
             }
             return true;
         } else if (themeDescriptor == null
                 && elementName.equals(IWorkbenchRegistryConstants.TAG_CATEGORYDEFINITION)) {
             ThemeElementCategory definition = readCategory(element);
             if (definition != null) {
                 if (!categoryDefinitions.contains(definition)) {
                     categoryDefinitions.add(definition);
                     themeRegistry.add(definition);
                 }
             }
             return true;
         } else if (element.getName().equals(IWorkbenchRegistryConstants.TAG_THEME)) {
             if (themeDescriptor != null) {
                 logError(element, RESOURCE_BUNDLE
                         .getString("Themes.badNesting")); //$NON-NLS-1$
 } else {
                 themeDescriptor = readTheme(element);
                 if (themeDescriptor != null) {
                     readElementChildren(element);
                     themeDescriptor = null;
                 }
                 return true;
             }
         } else if (themeDescriptor != null
                 && elementName.equals(IWorkbenchRegistryConstants.TAG_DESCRIPTION)) {
             themeDescriptor.setDescription(element.getValue());
             return true;
         } else if (elementName.equals(IWorkbenchRegistryConstants.TAG_DATA)) {
             String name = element.getAttribute(IWorkbenchRegistryConstants.ATT_NAME);
             String value = element.getAttribute(IWorkbenchRegistryConstants.ATT_VALUE);
             if (name == null || value == null) {
                 logError(element, RESOURCE_BUNDLE.getString("Data.badData")); //$NON-NLS-1$
 } else {
                 if (themeDescriptor != null) {
                     themeDescriptor.setData(name, value);
                 } else {
                     themeRegistry.setData(name, value);
                     if (!dataMap.containsKey(name)) {
                         dataMap.put(name, value);
                     }
                 }
             }
             return true;
         } else if (elementName.equals(IWorkbenchRegistryConstants.TAG_CATEGORYPRESENTATIONBINDING)) {
             String categoryId = element.getAttribute(IWorkbenchRegistryConstants.ATT_CATEGORY_ID);
             String presentationId = element.getAttribute(IWorkbenchRegistryConstants.ATT_PRESENTATIONID);
             themeRegistry.addCategoryPresentationBinding(categoryId,
                     presentationId);
             return true;
         }

         return false;
     }

     /**
      * Read a font.
      *
      * @param element the element to read
      * @return the new font
      */
     private FontDefinition readFont(IConfigurationElement element) {
         String name = element.getAttribute(IWorkbenchRegistryConstants.ATT_LABEL);

         String id = element.getAttribute(IWorkbenchRegistryConstants.ATT_ID);

         String defaultMapping = element.getAttribute(IWorkbenchRegistryConstants.ATT_DEFAULTS_TO);

         String value = getPlatformSpecificFontValue(element
                 .getChildren(IWorkbenchRegistryConstants.TAG_FONTVALUE));
         if (value == null) {
             value = element.getAttribute(IWorkbenchRegistryConstants.ATT_VALUE);
         }

         if (value != null && defaultMapping != null) {
             logError(element, RESOURCE_BUNDLE.getString("Fonts.badDefault")); //$NON-NLS-1$
 return null;
         }

         String categoryId = element.getAttribute(IWorkbenchRegistryConstants.ATT_CATEGORY_ID);

         boolean isEditable = true;
         String isEditableString = element.getAttribute(IWorkbenchRegistryConstants.ATT_IS_EDITABLE);
         if (isEditableString != null) {
             isEditable = Boolean.valueOf(isEditableString).booleanValue();
         }

         String description = null;

         IConfigurationElement[] descriptions = element
                 .getChildren(IWorkbenchRegistryConstants.TAG_DESCRIPTION);

         if (descriptions.length > 0) {
             description = descriptions[0].getValue();
         }

         return new FontDefinition(name, id, defaultMapping, value, categoryId,
                 isEditable, description);
     }

     /**
      * Check for platform specific font values. This will return the
      * "best match" for the current platform.
      *
      * @param elements the elements to check
      * @return the platform specific font, if any
      */
     private String getPlatformSpecificFontValue(IConfigurationElement[] elements) {
         return getFontValue(getBestPlatformMatch(elements));
     }

     /**
      * Gets the font valu from the value attribute.
      *
      * @param element the element to check
      * @return the font string
      */
     private String getFontValue(IConfigurationElement element) {
         if (element == null) {
             return null;
         }

         return element.getAttribute(IWorkbenchRegistryConstants.ATT_VALUE);
     }

     /**
      * Attempt to load the color value from the colorFactory attribute.
      *
      * @param element the element to load from
      * @return the value, or null if it could not be obtained
      */
     private String checkColorFactory(IConfigurationElement element) {
         String value = null;
         if (element.getAttribute(IWorkbenchRegistryConstants.ATT_COLORFACTORY) != null
                 || element.getChildren(IWorkbenchRegistryConstants.ATT_COLORFACTORY).length > 0) {
             try {
                 IColorFactory factory = (IColorFactory) element
                         .createExecutableExtension(IWorkbenchRegistryConstants.ATT_COLORFACTORY);
                 value = StringConverter.asString(factory.createColor());
             } catch (Exception e) {
                 WorkbenchPlugin.log(RESOURCE_BUNDLE
                         .getString("Colors.badFactory"), //$NON-NLS-1$
 WorkbenchPlugin.getStatus(e));
             }
         }
         return value;
     }

     /**
      * Read a theme.
      *
      * @param element the element to read
      * @return the new theme
      */
     protected ThemeDescriptor readTheme(IConfigurationElement element) {
         ThemeDescriptor desc = null;

         String id = element.getAttribute(ThemeDescriptor.ATT_ID);
         if (id == null) {
             return null;
         }
         //see if the theme has already been created in another extension
 desc = (ThemeDescriptor) themeRegistry.findTheme(id);
         //if not, create it
 if (desc == null) {
             desc = new ThemeDescriptor(id);
             themeRegistry.add(desc);
         }
         //set the name as applicable
 desc.extractName(element);
     
         return desc;
     }

     /**
      * Read the theme extensions within a registry.
      *
      * @param in the registry to read
      * @param out the registry to write to
      */
     public void readThemes(IExtensionRegistry in, ThemeRegistry out) {
         // this does not seem to really ever be throwing an the exception
 setRegistry(out);
         readRegistry(in, PlatformUI.PLUGIN_ID, IWorkbenchRegistryConstants.PL_THEMES);

         // support for old font definitions
 readRegistry(in, PlatformUI.PLUGIN_ID,
                 IWorkbenchRegistryConstants.PL_FONT_DEFINITIONS);
     }

     /**
      * Set the output registry.
      *
      * @param out the output registry
      */
     public void setRegistry(ThemeRegistry out) {
         themeRegistry = out;
     }
 }

